<?php

namespace aszx0413\yii2gii;

use Yii;

class Model
{
	/**
	 * @var string Models' directory
	 */
	private $dir = '';

	const CUSTOM_PROPERTIES_BEGIN = ' * ---------- CUSTOM PROPERTIES BEGIN ----------';
	const CUSTOM_PROPERTIES_END   = ' * ---------- CUSTOM PROPERTIES END   ----------';

	const CUSTOM_CODE_BEGIN = '// ---------- CUSTOM CODE BEGIN ----------';
	const CUSTOM_CODE_END   = '// ---------- CUSTOM CODE END   ----------';

	public function __construct($dir = '')
	{
		if (!empty($dir)) {
			$this->dir = $dir;
		} else {
			$this->dir = Yii::$app->basePath . '/models/';
		}
	}

	public function generate(Table $table, array $tables, array $config)
	{
		$ct = '';

		$parser = $this->parse($table);

		// --------------------------------------------------
		// 头部
		// --------------------------------------------------

		$ct .= "<?php" . "\n";
		$ct .= "namespace app\models;" . "\n";
		$ct .= "\n";
		$ct .= $parser['use'];
		$ct .= "\n";
		$ct .= "/**" . "\n";
		$ct .= " * @property int \$id" . "\n";
		$ct .= " * @property string \$created_at" . "\n";
		$ct .= " * @property string \$updated_at" . "\n";
		$ct .= " * @property int \$status" . "\n";
		$ct .= " *" . "\n";

		/* 以下是表字段 */

		foreach ($table->fields as $field) {
			if (strpos($field->type, 'INT') !== false) {
				$ct .= " * @property int \${$field->name} {$field->nameCn}" . "\n";
			} elseif (strpos($field->type, 'DECIMAL') !== false) {
				$ct .= " * @property float \${$field->name} {$field->nameCn}" . "\n";
			} else {
				$ct .= " * @property string \${$field->name} {$field->nameCn}" . "\n";
			}
		}

		$ct .= " *" . "\n";

		/* 以下是关联表属性 */

		$hasRelation = false;
		foreach ($table->fields as $field) {
			if ($relateTable = $field->getHasToTable($tables)) {
				$hasRelation = true;

				$ct .= " * @property {$relateTable->class} \${$field->asProperty()} {$relateTable->nameCn}" . "\n";
			}
		}
		foreach ($table->hasManyList as $many) {
			$hasRelation = true;

			$ct .= " * @property {$many->asClass()}[] \$" . $many->asProperty() . " {$table->nameCn}" . "\n";
		}
		if ($hasRelation) {
			$ct .= " *" . "\n";
		}

		/* 以下是自定义属性 */

		$ct .= self::CUSTOM_PROPERTIES_BEGIN;
		$ct .= $parser['customProperties'];
		$ct .= self::CUSTOM_PROPERTIES_END . "\n";

		$ct .= " */" . "\n";

		// --------------------------------------------------
		// 类定义
		// --------------------------------------------------

		$ct .= "class {$table->class} extends {$config['extendClass']} {$parser['implements']}" . "\n";
		$ct .= "{" . "\n";

		// ---------- tableName ----------

		$ct .= "    /**" . "\n";
		$ct .= "     * {@inheritdoc}" . "\n";
		$ct .= "     */" . "\n";
		$ct .= "    public static function tableName()" . "\n";
		$ct .= "    {" . "\n";
		$ct .= "        return '{$table->name}';" . "\n";
		$ct .= "    }" . "\n";
		$ct .= "\n";

		// ---------- relations ----------

		foreach ($table->fields as $field) {
			if ($relateTable = $field->getHasToTable($tables)) {
				$ct .= "    public function get{$relateTable->asGetXxx($field->name)}()" . "\n";
				$ct .= "    {" . "\n";
				$ct .= "        return \$this->hasOne({$relateTable->class}::class, ['id' => '{$field->name}']);" . "\n";
				$ct .= "    }" . "\n";
				$ct .= "\n";
			}
		}
		foreach ($table->hasManyList as $many) {
			$ct .= "    public function get" . ucfirst($many->asProperty()) . "()" . "\n";
			$ct .= "    {" . "\n";
			$ct .= "        return \$this->hasMany({$many->asClass()}::class, ['{$many->name}' => 'id']);" . "\n";
			$ct .= "    }" . "\n";
			$ct .= "\n";
		}

		// ---------- CUSTOM CODE ----------

		$ct .= '    ' . self::CUSTOM_CODE_BEGIN;
		$ct .= $parser['customCode'];
		$ct .= self::CUSTOM_CODE_END . "\n";

		$ct .= "}" . "\n";

		$modelFile = $this->dir . $table->class . '.php';
		file_put_contents($modelFile, $ct);
	}

	private function parse(Table $table)
	{
		$parser = [
			// use语句
			'use'              => '',
			// 自定义属性
			'customProperties' => "\n",
			// 接口语句
			'implements'       => '',
			// 自定义代码
			'customCode'       => "\n",
		];

		$modelFile = $this->dir . $table->class . '.php';

		if (file_exists($modelFile)) {
			$contents = file_get_contents($modelFile);
			$lines    = explode("\n", $contents);
			$cntLine  = count($lines);

			$parser['customProperties'] = $this->getStringBetween($contents, self::CUSTOM_PROPERTIES_BEGIN,
			                                                      self::CUSTOM_PROPERTIES_END);

			$parser['customCode'] = $this->getStringBetween($contents, self::CUSTOM_CODE_BEGIN, self::CUSTOM_CODE_END);

			for ($i = 0; $i < $cntLine; $i++) {
				$line = $lines[$i];

				if (str_starts_with($line, 'use ')) {
					$parser['use'] .= $line . "\n";
				}
				if (($implPos = strpos($line, ' implements ')) !== false) {
					$parser['implements'] = substr($line, $implPos);
				}
			}
		} else {
			$parser['use'] = 'use Yii;' . "\n";
		}

		return $parser;
	}

	private function getStringBetween($str, $from, $to)
	{
		$start = strpos($str, $from);
		if ($start === false) {
			return "\n";
		}

		$end = strripos($str, $to);

		return substr($str, $start + mb_strlen($from), $end - $start - mb_strlen($to));
	}
}
